#ifndef HELMETUART_H
#define HELMETUART_H

#include "HelmetGlobals.h"
#include "HelmetSystemConfig.h"
#include "HelmetTimers.h"
#include "HelmetI2C.h"

/**************************************************************************************
	Global Variables
**************************************************************************************/
// gps buffer
#define GPS_BUFFER_SIZE	400
char gpsbuffer[GPS_BUFFER_SIZE]; // gps data buffer can hold 2 LN's
unsigned short int gpsbufferreadpos = 0; // read pos of buffer
unsigned short int gpsbufferwritepos = 0; // write pos of buffer

// common to all GPS packets
char qualifier;
char identifier[2];

// gps message + info
char gpsmsg[200]; // max length == 198
unsigned short int gpsmsgpos = 0; // position of message being read

/**************************************************************************************
	Definitions
**************************************************************************************/
// Atom
#define ATOM UART1
#define ATOM_HANDLER 		void __ISR(_UART1_VECTOR,ipl4)IntUart1Handler(void)

// GPS
#define GPS UART2
#define GPS_HANDLER 		void __ISR(_UART2_VECTOR,ipl5)IntUART2Handler(void)

/**************************************************************************************
	Function Prototypes
**************************************************************************************/
void WriteAtom(const char *string);
void WriteGPS(const char *string);
void ConfigAtom( void );
void ConfigGPS( void );
unsigned short int GPSBufferDataAvailable();
char ReadGPSBuffer();
unsigned short int OutputGPSMessage();

/**************************************************************************************
	Functions
**************************************************************************************/
void ConfigAtom() {
	// Configuration
	UARTConfigure( ATOM, UART_ENABLE_PINS_TX_RX_ONLY);
    UARTSetFifoMode( ATOM, UART_INTERRUPT_ON_TX_NOT_FULL | UART_INTERRUPT_ON_RX_NOT_EMPTY);
    UARTSetLineControl( ATOM, UART_DATA_SIZE_8_BITS | UART_PARITY_NONE | UART_STOP_BITS_1);
    UARTSetDataRate( ATOM, GetPeripheralClock(), 38400 );
    UARTEnable( ATOM, UART_ENABLE_FLAGS(UART_PERIPHERAL | UART_RX | UART_TX));
    
    // Interrupt setup
    INTEnable( INT_SOURCE_UART_RX(ATOM), INT_ENABLED );
    INTSetVectorPriority( INT_VECTOR_UART(ATOM), INT_PRIORITY_LEVEL_4 );
    INTSetVectorSubPriority( INT_VECTOR_UART(ATOM), INT_SUB_PRIORITY_LEVEL_0 );	
}

void ConfigGPS() {
	// Configuration
	UARTConfigure( GPS, UART_ENABLE_PINS_TX_RX_ONLY );
    UARTSetFifoMode( GPS, UART_INTERRUPT_ON_TX_NOT_FULL | UART_INTERRUPT_ON_RX_NOT_EMPTY );
    UARTSetLineControl( GPS, UART_DATA_SIZE_8_BITS | UART_PARITY_NONE | UART_STOP_BITS_1 );
    UARTSetDataRate( GPS, GetPeripheralClock(), 38400 );
    UARTEnable( GPS, UART_ENABLE_FLAGS(UART_PERIPHERAL | UART_RX | UART_TX) );
    
    // Interrupt setup
	INTEnable( INT_SOURCE_UART_RX(GPS), INT_ENABLED );
    INTSetVectorPriority(INT_VECTOR_UART(GPS), INT_PRIORITY_LEVEL_5);
    INTSetVectorSubPriority(INT_VECTOR_UART(GPS), INT_SUB_PRIORITY_LEVEL_0);
}

// Write string to Atom
void WriteAtom(const char *string)
{
    while(*string != '\0')
    {
        while( !UARTTransmitterIsReady(ATOM) );
        UARTSendDataByte( ATOM, *string );
        string++;
        while( !UARTTransmissionHasCompleted(ATOM) );
    }
}

// Write char to Atom
void WriteCharAtom( char c ) {
	while( !UARTTransmitterIsReady(ATOM) );
	UARTSendDataByte( ATOM, c );
	while( !UARTTransmissionHasCompleted(ATOM) );	
}	

// Write string to GPS
void WriteGPS(const char *string)
{
    while(*string != '\0')
    {
        while( !UARTTransmitterIsReady(GPS) );
        UARTSendDataByte( GPS, *string );
        string++;
        while( !UARTTransmissionHasCompleted(GPS) );
    }
}

unsigned short int OutputGPSMessage() {
	// if packet is not a response, disregard and report not interpreted
	if( qualifier != 'R' ) {
		return 0;
	}
	if( identifier[0]=='S' && identifier[1]=='T' ) {
		// status message
		WriteAtom( "GPS" );
		WriteCharAtom( identifier[0] );
		WriteCharAtom( identifier[1] );
		WriteCharAtom( ' ' );
		if( gpsmsg[0] == '0' ) {
			if( gpsmsg[1] == '0' ) {
				WriteAtom( "Doing position fixes\r\n" );
			} else if( gpsmsg[1] == '1' ) {
				WriteAtom( "No GPS time\r\n" );
			} else if( gpsmsg[1] == '3' ) {
				WriteAtom( "PDOP too high\r\n" );
			} else if( gpsmsg[1] == '8' ) {
				WriteAtom( "No satellites\r\n" );
			} else if( gpsmsg[1] == '9' ) {
				WriteAtom( "1 satellite\r\n" );
			} else if( gpsmsg[1] == 'A' ) {
				WriteAtom( "2 satellites\r\n" );
			} else if( gpsmsg[1] == 'B' ) {
				WriteAtom( "3 satellites\r\n" );
			} else if( gpsmsg[1] == 'C' ) {
				WriteAtom( "Unreliable satellite\r\n" );
			} else {
				WriteAtom( "Unknown status\r\n" );
			}
		} else {
			WriteAtom( "Unknown status\r\n" );
		}
	} else if( identifier[0]=='L' && identifier[1]=='N' ) {
		// long navigation message
		short int svcount = (gpsmsg[51]-0x30)*10 + (gpsmsg[52]-0x30);
		short int fix = gpsmsg[63+4*svcount]-0x30;
		short int age = gpsmsg[64+4*svcount]-0x30;
		if( age==0 || fix==9 ) {
			// if data not available or no fix available, exit
			//return;
		}
		unsigned short int i;
		WriteAtom( "GPS" );
		WriteCharAtom( identifier[0] );
		WriteCharAtom( identifier[1] );
		WriteCharAtom( ' ' );
		// GPS time of day
		for( i=0 ; i<=7 ; i++ ) {
			if( i== 5 ) WriteCharAtom( '.' );
			WriteCharAtom( gpsmsg[i] );
		}
		// latitude
		WriteAtom( " " );
		for( i=8 ; i<=17 ; i++ ) {
			if( i==11 ) WriteCharAtom( '.' );
			WriteCharAtom( gpsmsg[i] );	
		}
		// longitude
		WriteAtom( " " );
		for( i=18 ; i<=28 ; i++ ) {
			if( i==22 ) WriteCharAtom( '.' );
			WriteCharAtom( gpsmsg[i] );	
		}
		WriteAtom( " " );
		// altitude
		for( i=29 ; i<=37 ; i++ ) {
			if( i==36 ) WriteCharAtom( '.' );
			WriteCharAtom( gpsmsg[i] );	
		}
		WriteAtom( " " );
		// horiz spd
		for( i=38 ; i<=41 ; i++ ) {
			if( i==41 ) WriteCharAtom( '.' );
			WriteCharAtom( gpsmsg[i] );	
		}
		WriteAtom( " " );
		// vert spd
		for( i=42 ; i<=46 ; i++ ) {
			if( i==46 ) WriteCharAtom( '.' );
			WriteCharAtom( gpsmsg[i] );	
		}
		WriteAtom( " " );
		// heading
		for( i=47 ; i<=50 ; i++ ) {
			if( i==50 ) WriteCharAtom( '.' );
			WriteCharAtom( gpsmsg[i] );	
		}
		WriteAtom( " " );
		WriteCharAtom( gpsmsg[63] );
		WriteAtom( "\r\n" );
	} else if( identifier[0]=='T' && identifier[1]=='M' ) {
		// time/date message		
		unsigned short int i;
		WriteAtom( "GPS" );
		WriteCharAtom( identifier[0] );
		WriteCharAtom( identifier[1] );
		WriteCharAtom( ' ' );
		// check if msg invalid
		if( gpsmsg[22] == '0' ) {
			WriteAtom( "Time not available\r\n" );
			return;
		}	
		// year
		for( i=13 ; i<=16; i++ ) {
			WriteCharAtom( gpsmsg[i] );
		}
		WriteCharAtom( '/' );
		// month
		for( i=11 ; i<=12 ; i++ ) {
			WriteCharAtom( gpsmsg[i] );
		}
		WriteCharAtom( '/' );
		// day
		for( i=9 ; i<=10 ; i++ ) {
			WriteCharAtom( gpsmsg[i] );
		}
		WriteCharAtom( ' ' );
		// hours
		for( i=0 ; i<=1 ; i++ ) {
			WriteCharAtom( gpsmsg[i] );
		}
		WriteCharAtom( ':' );
		// minutes
		for( i=2 ; i<=3 ; i++ ) {
			WriteCharAtom( gpsmsg[i] );
		}
		WriteCharAtom( ':' );
		// seconds
		for( i=4 ; i<=8 ; i++ ) {
			if( i==6 ) WriteCharAtom( '.' );
			WriteCharAtom( gpsmsg[i] );
		}
		WriteCharAtom( ' ' );
		// UTC offset
		for( i=17 ; i<=18 ; i++ ) {
			WriteCharAtom( gpsmsg[i] );
		}
		WriteCharAtom( ' ' );
		// fix mode
		WriteCharAtom( gpsmsg[19] );
		WriteAtom( "\r\n" );
	} else {
		// don't care
	}
}	

unsigned short int ParseGPSMessage() {
	char data;	
	if( !InterpretGPSMessage ) {
		// if interpreting is not happening, read as much as possible or until end of message
		while( GPSBufferDataAvailable() ){
			data = ReadGPSBuffer();
			if( data!='\0' ) {
				if( data == '>' ) {
					// beginning of message
					gpsmsgpos = 1;	
				} else if( data == '<' ) {
					// end of message
					gpsmsgpos = 1;
					DoInMain( InterpretGPSMessage );
					return 0; // potentially more to read later (try again after interpreting)
				} else if( data == '\r' || data == '\n' ) {
					// do nothing for whitespace
				} else {
					// not beginning or end
					if( gpsmsgpos == 1 ) { // 1
						// store qualifier for easy access
						qualifier = data;
					} else if( gpsmsgpos==2 || gpsmsgpos==3 ) { // 2 & 3
						// store identifier for easy access
						identifier[gpsmsgpos-2] = data;
					} else if( gpsmsgpos>3 ) { // 4+
						// store message data
						gpsmsg[gpsmsgpos-4] = data;
					} else {
						// do nothing if not >1
						WriteAtom( "!\r\n" );
					}
					gpsmsgpos++;
				}	
			} else {
				WriteAtom( "Warng GPS buffer data not available" );
			}
		}
		return 1; // success (nothing more to read)
	} else {
		// if a message is still being interpreted, don't try to read data
		return 0; // not successful (try again later)
	}		
}	

unsigned short int GPSBufferDataAvailable() {
	if( gpsbufferwritepos != gpsbufferreadpos ) {
		return 1;
	} else {
		return 0;
	}
}	

char ReadGPSBuffer() {
	if( GPSBufferDataAvailable ) {
		if( gpsbufferreadpos == GPS_BUFFER_SIZE-1 ) {
			gpsbufferreadpos = 0;
		} else {
			gpsbufferreadpos++;
		}
		return gpsbuffer[gpsbufferreadpos];
	} else {
		return '\0';
	}
}

void WriteGPSBuffer( char c ) {
	if( gpsbufferwritepos == GPS_BUFFER_SIZE-1 ) {
		gpsbufferwritepos = 0;
	} else {
		gpsbufferwritepos++;
	}
	gpsbuffer[gpsbufferwritepos] = c;
}	

/**************************************************************************************
	Interrupt handling routines
**************************************************************************************/
// Note: Priority 1 (lowest) to 7 (highest)

/**************************************
		ATOM (UART 1)
			Priority 4
**************************************/
ATOM_HANDLER
{
	unsigned int atomdata;
		
	if(( INTGetFlag( INT_SOURCE_UART_RX(ATOM) ) )) {
		
		// Get Atom data byte
		atomdata = UARTGetDataByte( ATOM );
		
		// Echo Atom data byte to GPS
		UARTSendDataByte(GPS, atomdata );
		
		// Toggle LED to indicate UART activity
		SetAtomLED();
		
		// Make sure the flag is cleared
		INTClearFlag( INT_SOURCE_UART_RX(ATOM) );
	}
	
	// Don't care about TX interrupt
	if(( INTGetFlag( INT_SOURCE_UART_TX(ATOM) ) )) {
		INTClearFlag( INT_SOURCE_UART_TX(ATOM) );
	}
}	

/**************************************
		GPS (UART 2)
			Priority 5
**************************************/
GPS_HANDLER
{
	unsigned int gpsdata;
	
	// Is this an RX interrupt?
	if(( INTGetFlag( INT_SOURCE_UART_RX(GPS) ) )) {

		// Get GPS data byte
		gpsdata = UARTGetDataByte( GPS );
		
		// Echo GPS data byte to Atom
		//UARTSendDataByte( ATOM, gpsdata );
		
		// write data to the buffer and tell main to process it
		WriteGPSBuffer( gpsdata );
		DoInMain( ReadGPSMessage );
		
		// If the second is old,
		//   initiate a new second
		//   instruct main to perform adc conversion
		// Note: this effectively 'debounces' the multiple
		//       characters coming in from the GPS. The
		//       first new character begins a 'new second'.
		//       The timer sets the second as 'old' just 
		//       before the new second is expected to start
		//       long after the GPS will have sent its data.
		if( isOldSecond() ) {
			startNewSecond();
			GPSAlive = 2; // 'I am alive'
		}
		
		// Toggle LED to indicate UART activity
		SetGPSLED();
		
		// Make sure the flag is cleared
		INTClearFlag( INT_SOURCE_UART_RX(GPS) );
	}

	// Don't care about TX interrupt
	if ( INTGetFlag(INT_SOURCE_UART_TX(GPS)) ) {
		INTClearFlag(INT_SOURCE_UART_TX(GPS));
	}
}

#endif